package com.altio.swing;


import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.util.HashMap;
import java.util.Iterator;

/**
 * Simple layout manager that places components in a given corner of the
 * container or simply allowed to fill the entire container. Only components
 * added via addLayoutComponent are positioned. Components placed in the same
 * corner will just be allowed to overlap.
 * 
 * @author tomm
 * @altioapi all
 * 
 */
public class CornerLayoutManager implements LayoutManager {
  public static final String FILL = "FILL";

  public static final String TOPLEFT = "TOPLEFT";

  public static final String TOPRIGHT = "TOPRIGHT";

  public static final String BOTTOMLEFT = "BOTTOMLEFT";

  public static final String BOTTOMRIGHT = "BOTTOMRIGHT";

  /** Components that we will actually lay out */
  private HashMap components = new HashMap();
  /** Indentation that we will apply between a component and its corner */
  private int indent;

  private Dimension minimum = new Dimension();
  private Dimension preferred = new Dimension();

  synchronized public void addLayoutComponent(String position, Component component) {
    components.put(component, position);
  }

  synchronized public void layoutContainer(Container container) {
    calculateLayout(container);
  }
  
  private void calculateLayout(Container container) {
    calculateLayout(container, true);
  }
  
  /**
   * Does the layout.  If apply is set to false then the layout
   * is not applied to the controls so this method can be used to calculate
   * the preferred and minimum size for caching.
   * @param container
   * @param apply
   */
  private void calculateLayout(Container container, boolean apply) {
    
    // Use local preferred and minimum sizes in case the layout gets called
    // recursively somehow.
    Dimension tempPreferred = new Dimension();
    Dimension tempMinimum = new Dimension();
    
    Iterator nextComponent = components.keySet().iterator();
    if (nextComponent.hasNext()) {
      Component currentComponent = (Component) nextComponent.next();

      Dimension currentMinimumSize = currentComponent.getMinimumSize();
      
      if(currentMinimumSize != null) {
        tempMinimum.width = Math.min(currentMinimumSize.width, tempMinimum.width);
        tempMinimum.height = Math.min(currentMinimumSize.height, tempMinimum.height);
      }
      
      Dimension currentPreferredSize = currentComponent.getPreferredSize();
      
      if(currentPreferredSize != null) {
        tempPreferred.width = Math.min(currentPreferredSize.width, tempPreferred.width);
        tempPreferred.height = Math.min(currentPreferredSize.height, tempPreferred.height);
      }
    }
    // For each component we are going to layout
    nextComponent = components.keySet().iterator();
    while (nextComponent.hasNext()) {
      Component currentComponent = (Component) nextComponent.next();

      Dimension currentMinimumSize = currentComponent.getMinimumSize();
      
      if(currentMinimumSize != null) {
        tempMinimum.width = Math.max(currentMinimumSize.width, tempMinimum.width);
        tempMinimum.height = Math.max(currentMinimumSize.height, tempMinimum.height);
      }
      
      Dimension currentPreferredSize = currentComponent.getPreferredSize();
      
      if(currentPreferredSize != null) {
        tempPreferred.width = Math.max(currentPreferredSize.width, tempPreferred.width);
        tempPreferred.height = Math.max(currentPreferredSize.height, tempPreferred.height);
        
        if(apply) {
          // Set the component to the desired size
          currentComponent.setSize(currentComponent.getPreferredSize());
        }
      }
      
      if(apply) {
        // Get the desired position of the component
        String position = (String) components.get(currentComponent);
        if (position != null) {
          // Is the component filling the container
          if (position.equals(FILL)) {
            // Fill the container
            currentComponent.setBounds(0, 0, container.getWidth(), container.getHeight());
          }
          else if (position.equals(TOPLEFT)) {
            // Put it in the top left
            currentComponent.setLocation(indent, indent);
  
          }
          else if (position.equals(TOPRIGHT)) {
            // Put it in the top right
            currentComponent.setLocation((container.getWidth() - currentComponent.getWidth()) - indent, indent);
          }
          else if (position.equals(BOTTOMLEFT)) {
            // etc
            currentComponent.setLocation(indent, (container.getHeight() - currentComponent.getHeight()) - indent);
          }
          else if (position.equals(BOTTOMRIGHT)) {
            currentComponent.setLocation(container.getWidth() - currentComponent.getWidth() - indent,
                (container.getHeight() - currentComponent.getHeight()) - indent);
          }
        }
      }
    }
    
    preferred = tempPreferred;
    minimum = tempMinimum;
  }

  public Dimension minimumLayoutSize(Container container) {
    if(minimum == null || !container.isValid()) {
      calculateLayout(container, false);
    }
    return minimum;
  }

  public Dimension preferredLayoutSize(Container container) {
    if(preferred == null || !container.isValid()) {
      calculateLayout(container, false);
    }
    return preferred;
  }

  synchronized public void removeLayoutComponent(Component component) {
    components.remove(component);
  }

  /**
   * Indentation that we will apply between a component and its corner
   * 
   * @param i
   */
  public void setIndent(int i) {
    indent = i;
  }
}
